#ifndef SAMPLE_HEADER_HPP 
#define SAMPLE_HEADER_HPP 

#include <cstdlib>  /* exit system malloc atoi rand */
#include <iostream> /* std::cout std::endl */
#include <unistd.h> /* read sleep NULL close */
#include <mutex>
#include <condition_variable>

#include "types.hpp"

namespace SAMPLE_CODE
{

class DataS1
{
public:
    INT32 v1 {INT32_0};
    INT64 v2 {INT64_0};
};

class DataS2
{
public:
    UINT32 v1 {UINT32_0};
    UINT64 v2 {UINT64_0};
};

class DataP
{
public:
    std::shared_ptr<DataS1> p1 {std::make_shared<DataS1>()};
    std::shared_ptr<DataS2> p2 {std::make_shared<DataS2>()};
};

class DataType1 final
{
public:
    DataType1() = default;
    ~DataType1() = default;
    DataType1(DataType1 const &) = default;
    DataType1(DataType1 const && _rfs) noexcept
        /* A18-9-2 */
      : a{_rfs.a}
      , b{_rfs.b}
      {}
    DataType1 &operator=(DataType1 const &) & = delete;
    DataType1 &operator=(DataType1 const &&) & = delete;

private:
    std::shared_ptr<DataS1> a {std::make_shared<DataS1>()};
    std::shared_ptr<DataS2> b {std::make_shared<DataS2>()};
};

class DataType2 final
{
public:
    DataType2() = default;
    ~DataType2() = default;
    DataType2(DataType2 const &) = default;
    DataType2(DataType2 const && _rfs) noexcept
        /* A18-9-3 */
      : a{std::move(_rfs.a)}
      , b{std::move(_rfs.b)}
      {}
    DataType2 &operator=(DataType2 const &) & = delete;
    DataType2 &operator=(DataType2 const &&) & = delete;

private:
    std::shared_ptr<DataS1> a {std::nullptr_t()};
    std::shared_ptr<DataS2> b {std::nullptr_t()};
};

class DataType3 final
{
public:
    DataType3() = default;
    ~DataType3() = default;
    DataType3(DataType3 const &) = default;
    DataType3(DataType3 && _rfs) noexcept
        /* A8-4-8 */
      : a{std::move(_rfs.a)}
      , b{std::move(_rfs.b)}
      {}
    DataType3 &operator=(DataType3 const &) & = delete;
    DataType3 &operator=(DataType3 const &&) & = delete;

private:
    std::shared_ptr<DataS1> a {std::nullptr_t()};
    std::shared_ptr<DataS2> b {std::nullptr_t()};
};

class SampleClassHelper final
{
public:
    explicit SampleClassHelper(std::shared_ptr<DataS1> const & _inData1
                              ,std::shared_ptr<DataS2> const & _inData2
                              ,UINT32                 const   _cnt) noexcept;
    ~SampleClassHelper() noexcept = default;
private:
    SampleClassHelper() = delete;
    SampleClassHelper(SampleClassHelper const &) = delete;
    SampleClassHelper(SampleClassHelper const &&) = delete;
    SampleClassHelper &operator=(SampleClassHelper const &) & = delete;
    SampleClassHelper &operator=(SampleClassHelper const &&) & = delete;

private:
    UINT32                  mCnt;
    std::shared_ptr<DataS1> mData1;
    std::shared_ptr<DataS2> mData2;
};

class SampleClass1 final
{
public:
    explicit SampleClass1(std::shared_ptr<DataS1> const & _inData1
                         ,std::shared_ptr<DataS2> const & _inData2
                         ,UINT32                 const   _cnt) noexcept;
    ~SampleClass1() = default;
    void Start() noexcept;

private:
    SampleClass1() = delete;
    SampleClass1(SampleClass1 const &) = delete;
    SampleClass1(SampleClass1 const &&) = delete;
    SampleClass1 &operator=(SampleClass1 const &) & = delete;
    SampleClass1 &operator=(SampleClass1 const &&) & = delete;

private:
    INT32 const             INTERVAL;
    BOOL                    mRun;
    UINT32                  mCnt;
    std::mutex              mSleepMutex;
    std::condition_variable mSleepCV;
    std::shared_ptr<DataS1> mData1;
    std::shared_ptr<DataS2> mData2;
    std::shared_ptr<SampleClassHelper> mHelper; 
};

template<typename T>
void sample_func_4(T const _val) noexcept
{
    /* A5-1-1 */
    constexpr size_t NUM_OF_BYTES { sizeof(T) };
    static_cast<void>(std::endl(std::cout << "NUM_OF_BYTES: " << NUM_OF_BYTES
                                          << " sizeof(val): " << sizeof(_val)));
}

}

#endif // SAMPLE_HEADER_HPP
